﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.ComponentModel;


namespace WindowsClient
{
    public class UdpPacketReceivedEventArgs : EventArgs
    {
        public string Message { get; set; }
        public EndPoint Source { get; set; }

        public UdpPacketReceivedEventArgs(byte[] data, int count, EndPoint source)
        {
            this.Message = Encoding.UTF8.GetString(data, 0, count);
            this.Source = source;
        }
    }

    class UDPMulticastConnector
    {

        private IPAddress mcastAddress;
        private int mcastPort;
        private Socket mcastSocket;
        private MulticastOption mcastOption;
        BackgroundWorker worker;

        /// <summary>
        /// Occurs when a packet is received.
        /// </summary>
        public event EventHandler<UdpPacketReceivedEventArgs> PacketReceived;


        public UDPMulticastConnector(string address, int port)
        {
            mcastAddress = IPAddress.Parse(address);
            mcastPort = port;
            worker = new BackgroundWorker();
            worker.DoWork += new DoWorkEventHandler(worker_DoWork);
        }

        void worker_DoWork(object sender, DoWorkEventArgs e)
        {
            ReceiveBroadcastMessages();
        }

        private void MulticastOptionProperties()
        {
            Console.WriteLine("Current multicast group is: " + mcastOption.Group);
            Console.WriteLine("Current multicast local address is: " + mcastOption.LocalAddress);
        }

        public void StartMulticast()
        {

            if (worker.IsBusy)
                return;

            try
            {
                mcastSocket = new Socket(AddressFamily.InterNetwork,
                                         SocketType.Dgram,
                                         ProtocolType.Udp);

                IPAddress localIPAddr = GetLocalIp();
                EndPoint localEP = (EndPoint)new IPEndPoint(localIPAddr, mcastPort);

                mcastSocket.Bind(localEP);


                // Define a MulticastOption object specifying the multicast group  
                // address and the local IPAddress. 
                // The multicast group address is the same as the address used by the server.
                mcastOption = new MulticastOption(mcastAddress, localIPAddr);

                mcastSocket.SetSocketOption(SocketOptionLevel.IP,
                                            SocketOptionName.AddMembership,
                                            mcastOption);

                worker.RunWorkerAsync();
            }

            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }
        }

        public void Stop()
        {
            if(worker.IsBusy)
                worker.CancelAsync();
        }

        public void ReceiveBroadcastMessages()
        {
            bool done = false;
            byte[] bytes = new Byte[256];
            IPEndPoint groupEP = new IPEndPoint(mcastAddress, mcastPort);
            EndPoint remoteEP = (EndPoint)new IPEndPoint(IPAddress.Any, 0);

            try
            {
                while (!done)
                {
                    //Console.WriteLine("Waiting for multicast packets.......");
                    //Console.WriteLine("Enter ^C to terminate.");

                    //for (int i = 0; i < bytes.Length; ++i)
                    //    if (0 == bytes[i])
                    //        break;
                    //    else
                    //        bytes[i] = 0;

                    int bytescount = mcastSocket.ReceiveFrom(bytes, ref remoteEP);

                    EventHandler<UdpPacketReceivedEventArgs> handler = this.PacketReceived;

                    if (handler != null)
                    {
                        handler(this, new UdpPacketReceivedEventArgs(bytes, bytescount, remoteEP));
                    }
                    
                    //Console.WriteLine("Received broadcast from {0} :\n {1}\n",
                    //  groupEP.ToString(),
                    //  Encoding.ASCII.GetString(bytes, 0, bytes.Length));


                }

                mcastSocket.Close();
            }

            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }
        }

        IPAddress GetLocalIp()
        {
            IPHostEntry host;
            IPAddress localIP = null;
            host = Dns.GetHostEntry(Dns.GetHostName());
            foreach (IPAddress ip in host.AddressList)
            {
                if (ip.AddressFamily.ToString() == "InterNetwork")
                {
                    localIP = ip;
                }
            }
            Console.WriteLine("Current IP is " + localIP.ToString());
            return localIP;
        }

    }
}
